#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>
#include "Mutex.hpp"
#include "Cond.hpp"

namespace BlockQueueModule
{
    using namespace LockModule;
    using namespace CondModule;

    static const size_t gcap = 3;

    template<typename T>
    class BlockQueue
    {
    private:
        bool IsFull() { return _q.size() == _cap; }
        bool IsEmpty() { return _q.empty(); }

    public:
        BlockQueue(size_t cap = gcap) 
            :_cap(cap)
            ,_cwait_num(0)
            ,_pwait_num(0)
        {   
            std::cout << "BlockQueue 初始化，容量: " << _cap << std::endl;
        }

        void EnQueue(const T& in) //生产者
        {
            LockGuard lockguard(_mutex);
            //你想放数据就能放数据吗？？ 生产数据是有条件的！
            // 结论1:在临界区中等待是必然的
            while(IsFull())
            {
                std::cout << "生产者进入等待... 当前队列大小: " << _q.size() << std::endl;

                //2, 等是， 释放_mutex
                ++_pwait_num;
                _productor_cond.Wait(_mutex);
                --_pwait_num;
                //返回线程被唤醒，&& 重新申请并持有锁（他会在临界区醒来）

                std::cout << "生产者被唤醒... 当前队列大小: " << _q.size() << std::endl;
            }

            _q.push(in);
            std::cout << "生产者放入数据，当前队列大小: " << _q.size() << std::endl;
            //肯定有数据
            if(_cwait_num)
            {
                std::cout << "叫醒消费者..." <<std::endl;
                _consumer_cond.Notify();
            }

        }

        void Pop(T* out)//消费者
        {
            LockGuard lockgud(_mutex);
            while(IsEmpty())
            {
                std::cout << "消费者进入等待... 当前队列大小: " << _q.size() << std::endl;

                ++_cwait_num;
                _consumer_cond.Wait(_mutex);
                --_cwait_num;

                std::cout << "消费者被唤醒.... 当前队列大小: " << _q.size() << std::endl;
            }

            *out = _q.front();
            _q.pop();
            std::cout << "消费者取出数据，当前队列大小: " << _q.size() << std::endl;
            if(_pwait_num)
            {
                std::cout << "唤醒生产者..." <<std::endl;
                _productor_cond.Notify();
            }

        }

        ~BlockQueue()
        {
        }

        
    private:
        std::queue<T> _q;       //保存数据的容器， 临界资源
        size_t _cap;               //bq的最大容器
        Mutex _mutex;          //互斥

        Cond _productor_cond;  //生产者条件变量
        Cond _consumer_cond;   //消费者条件变量

        int _cwait_num;
        int _pwait_num;
    };


















    // // version 1 
    // static const int gcap = 10;

    // template<typename T>
    // class BlockQueue
    // {
    // private:
    //     bool IsFull()
    //     {
    //         return _q.size() == _cap;
    //     }

    //     bool IsEmpty()
    //     {
    //         return _q.empty();
    //     }

    // public:
    //     BlockQueue(int cap = gcap)
    //         :_cap(cap)
    //         ,_cwait_num(0)
    //         ,_pwait_num(0)
    //     {
    //         pthread_mutex_init(&_mutex, nullptr);
    //         pthread_cond_init(&_productor_cond, nullptr);
    //         pthread_cond_init(&_consumer_cond, nullptr);
    //     }

    //     void EnQueue(const T& in) //生产者
    //     {
    //         pthread_mutex_lock(&_mutex);
    //         //你想放数据，就能放数据吗？生产数据是有条件的！
    //         //结论一：在临界区等待是必然的（目前）
    //         while(IsFull())//5,对条件进行判断， 为了防止伪唤醒， 我们通常使用while进行判断
    //         {
    //             std::cout<< "生产者进入等待..." <<std::endl;

    //             // 2,等是， 释放_mutex
    //             _pwait_num++;;
    //             pthread_cond_wait(&_productor_cond, &_mutex); //wait 的时候， 必定是持有锁的!!是有问题的
    //             _pwait_num--;
    //             //3,返回，线程被重新被唤醒&&重新持有锁（她会在临界区醒来！）

    //             std::cout<< "生产者被唤醒..." <<std::endl;
    //         }
    //         // 4，if(IsFull())不满足 || 线程被唤醒
    //         _q.push(in); //生产

    //         //肯定有数据
    //         if(_cwait_num)
    //         {
    //             std::cout<< "叫醒消费者" <<std::endl;
    //             pthread_cond_signal(&_consumer_cond);
    //         }
    //         pthread_mutex_unlock(&_mutex);

    //     }

    //     void Pop(T *out) //消费者
    //     {
    //         pthread_mutex_lock(&_mutex);
    //         while(IsEmpty())
    //         {
    //             std::cout<< "消费者进入等待..." <<std::endl;
    //             _cwait_num++;
    //             pthread_cond_wait(&_consumer_cond, &_mutex);
    //             _cwait_num--;
    //             std::cout<< "消费者被唤醒..." <<std::endl;
    //         }
    //         //4, if(IsEmpty())不满足 || 线程被唤醒
    //         *out = _q.front();
    //         _q.pop();

    //         //肯定有空间
    //         if(_pwait_num)
    //         {
    //             std::cout<< "叫醒生产者" <<std::endl;
    //             pthread_cond_signal(&_productor_cond);
    //         }

    //         pthread_mutex_unlock(&_mutex);
    //     }

    //     ~BlockQueue()
    //     {
    //         pthread_mutex_destroy(&_mutex);
    //         pthread_cond_destroy(&_productor_cond);
    //         pthread_cond_destroy(&_consumer_cond);
    //     }

    // private:
    //     std::queue<T> _q;               //保存数据的容器,临界资源
    //     int _cap;                       //bq最大容量
    //     pthread_mutex_t _mutex;         //互斥
    //     pthread_cond_t _productor_cond; //生产者条件变量
    //     pthread_cond_t _consumer_cond;  //消费者条件变量

    //     int _cwait_num;
    //     int _pwait_num;
    // };

}